/**
 * 深度拷贝
 * @param {*} cloneObj 被拷贝的对象
 * @returns 拷贝后的对象
 */
function deepClone(cloneObj) {
  let newObj = arguments[1] || {}
  const toStr = Object.prototype.toString
  const ARRAY_TYPE = '[object Array]'

  for (var key in cloneObj) {
    if (typeof cloneObj[key] === 'object' && cloneObj[key] !== null) {
      if (toStr.call(cloneObj[key]) === ARRAY_TYPE) {
        // 数组处理
        newObj[key] = deepClone(cloneObj[key], [])
      } else {
        // 对象处理
        newObj[key] = deepClone(cloneObj[key])
      }
    } else {
      // 原始值处理 及 历史遗留问题null
      newObj[key] = cloneObj[key]
    }
  }
  return newObj
}

/**
 * 鉴定对象类型
 * @param {*} obj 被鉴定对象
 * @returns
 */
function getObjType(obj) {
  //tostring会返回对应不同的标签的构造函数
  var toString = Object.prototype.toString
  var map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  }
  if (obj instanceof Element) {
    return 'element'
  }
  return map[toString.call(obj)]
}

/**
 * 防抖函数
 * @param {*} fn 需要防抖的函数
 * @param {*} time 延迟时间（毫秒）
 * @param {*} isFirst 是否首次防抖
 */
function debounce(fn, time, isFirst) {
  var t = null,
    res

  return function () {
    var args = arguments,
      _self = this

    t && clearTimeout(t)

    if (isFirst) {
      var flag = !t
      t = setTimeout(function () {
        t = null
      }, time)

      flag && (res = fn.apply(_self, args))
    } else {
      t = setTimeout(function () {
        res = fn.apply(_self, args)
      }, time)
    }

    return res
  }
}

// 节流函数（时间戳）
function throttle(fn, delay) {
  var begin = Date.now()
  return function () {
    var _self = this,
      args = arguments,
      end = Date.now()
    if (end - begin >= delay) {
      fn.apply(_self, args)
      begin = Date.now()
    }
  }
}

// 节流函数（定时器）
function throttle_T(fn, delay) {
  var t = null
  return function () {
    var _self = this,
      args = arguments
    if (!t) {
      t = setTimeout(function () {
        fn.apply(_self, args)
        clearTimeout(t)
        t = null
      }, delay)
    }
  }
}

/**
 * 简易 Proxy 重写
 * @param {*} target 目标对象
 * @param {*} handle 操作配置
 *  get：[[GET]]
 *  set: [[SET]]
 * @returns 代理对象
 */
function myProxy(target, handle) {
  var _target = deepClone(target)

  Object.keys(_target).forEach((prop) => {
    Object.defineProperty(_target, prop, {
      get() {
        return handle.get && handle.get(target, prop)
      },
      set(newValue) {
        return handle.set && handle.set(target, prop, newValue)
      },
    })
  })

  return _target
}

// 动画优化 REF 替代 定时器
// 仅用于 window DOM操作，动画帧
const REF = {
  intervalTimer: null,
  timeoutTimer: null,
  setTimeout(cb, interval) {
    let now = Data.now
    let stime = now()
    let etime = stime
    let loop = () => {
      this.timeoutTimer = requestAnimationFrame(loop)
      etime = now()
      if (etime - stime >= interval) {
        cb()
        cancelAnimationFrame(this.timeoutTimer)
      }
    }
    this.timeoutTimer = requestAnimationFrame(loop)
    return this.timeoutTimer
  },
  cancelTimeOut() {
    cancelAnimationFrame(this.timeoutTimer)
  },
  setInterval(cb, interval) {
    let now = Date.now
    let stime = now()
    let etime = stime
    let loop = () => {
      ;(this.intervalTimer = requestAnimationFrame(loop)), (etime = now())
      if (etime - stime >= interval) {
        ;(stime = now()), (etime = stime), cb()
      }
    }
    this.intervalTimer = requestAnimationFrame(loop)
    return this.intervalTimer
  },
  cancelInterval() {
    cancelAnimationFrame(this.intervalTimer)
  },
}

/**
 * DOMContentLoaded 兼容低版本
 * @param {*} fn 回调函数
 */
function DOMReady(fn) {
  if (document.addEventListener) {
    // 由于DOMContentLoaded没有【句柄】形式
    document.addEventListener(
      'DOMContentLoader',
      function () {
        document.removeEventListener(
          'DOMContentLoader',
          arguments.callee,
          false
        )
        fn()
      },
      false
    )
  } else if (document.attachEvent) {
    document.attachEvent('onreadystatechange', function () {
      if (this.readyState === 'complete') {
        document.detachEvent('onreadystatechange', arguments.callee)
        fn()
      }
    })
  }

  if (
    document.documentElement.doSroll &&
    typeof window.frameElement === 'undefined'
  ) {
    try {
      document.documentElement.doSroll('left')
    } catch (e) {
      return setTimeout(arguments.callee, 20)
    }
  }
}

/**
 * 组合偏函数、纯函数及柯里化函数(pointfree)
 * @returns 组合函数
 */
function compose() {
  const args = [].slice.call(arguments)
  return function (x) {
    return args.reduceRight(function (params, callback) {
      return callback(params)
    }, x)
  }
}

/**
 * 柯里化函数
 * @param { Function } fn 柯里化的函数
 * @param { Number } len 参数长度（函数内部递归使用）
 */
function curry(fn, len) {
  var len = len || fn.length

  var func = function (fn) {
    var _arg = [].slice.call(arguments, 1) // 第一位是回调函数
    return function () {
      var newArg = _arg.concat([].slice.call(arguments))
      return fn.apply(this, newArgs)
    }
  }

  return function () {
    var argLen = arguments.length

    if (argLen < len) {
      var formateArr = [fn].concat([].slice.call(arguments))
      return curry(func.apply(this, formateArr), len - argLen)
    } else {
      return fn.apply(this, arguments)
    }
  }
}

/**
 * @description 根据传⼊的 key 获取 url 中对应的 value
 * @param {*} key
 * @param {*} url ⾮必填(若⽆，从当前⻚⾯链接中搜索)
 * @returns {string|undefined} 获取不到返回undefined
 */
 export const getUrlValue = (key, url) => {
  let locationHref = url !== undefined ? url : location.href
  let locationSearch = locationHref.substr(locationHref.indexOf('?') + 1)
  if (locationSearch.includes('#')) locationSearch = locationSearch.split('#')[0]
  let locationSearchArr = locationSearch.split('&')
  let locationSearchObj = {}
  for (let i = 0; i < locationSearchArr.length; i++) {
    locationSearchObj[locationSearchArr[i].split('=')[0]] = locationSearchArr[i].split('=')[1]
  }
  return locationSearchObj[key]
}